#include "graphic.h"

#include <linux/kernel.h>
#include <stdint.h>

#include "fonts.h"

static void TFT_SetWindow(uint16_t xStart, uint16_t yStart, uint16_t xEnd, uint16_t yEnd);
static void TFT_WriteData(u16 data);

void boxfill8(int xsize, u16 c, int x0, int y0, int x1, int y1) {
    int x, y;
    TFT_SetWindow(x0, y0, x1, y1);
    for (x = x0; x <= x1; ++x) {
        for (y = y0; y <= y1; ++y) {
            TFT_WriteData(c);
        }
    }
}

void init_screen(int xsize, int ysize) { boxfill8(xsize, COL8_009999, 0, 0, xsize - 1, ysize - 1); }

/**
 * @brief 输入一个字符
 *
 * @param buf 显示的起始位置
 * @param xsize 屏幕的宽度，因为要换行显示，需要知道宽度
 * @param x 位置x
 * @param y 位置y
 * @param color 字体颜色
 * @param c 字符
 */
static void put_font8(int xsize, int x, int y, u16 color, u16 bc, const char c) {
    int i, j;
    u8 *font8 = hankaku + c * 16;
    // 计算一下显示范围
    int xend = x + 7;
    int yend = y + 15;
    if (xend > xsize) {
        xend = xsize;
    }
    TFT_SetWindow(x, y, xend, yend);
    // 开始显示
    for (i = 0; i < 16; ++i) {
        u8 tmp = font8[i];
        for (j = 0; j < 8; ++j) {
            if ((tmp & (0x80 >> j)) > 0) {
                TFT_WriteData(color);
            } else {
                TFT_WriteData(bc);
            }
        }
    }
}

/**
 * @brief 显示一个字符串
 *
 * @param buf 显示的起始位置
 * @param xsize 屏幕的宽度，因为要换行显示，需要知道宽度
 * @param x 位置x
 * @param y 位置y
 * @param color 字体颜色
 * @param s 字符串
 */
void put_font8_str(int xsize, int x, int y, u16 color, u16 bc, const char *s) {
    int i;
    for (i = 0; s[i] != '\0'; ++i) {
        put_font8(xsize, x + 8 * i, y, color, bc, s[i]);
    }
}

/* TFT地址结构体 */
typedef struct {
    u16 TFT_CMD;
    u16 TFT_DATA;
} TFT_TypeDef;
/* 使用NOR/SRAM的 Bank1.sector4,地址位HADDR[27,26]=11 A10作为数据命令区分线 */
/* 注意设置时STM32内部会右移一位对其! 111110=0X3E */
#define TFT_BASE ((uint32_t)(0x6C000000 | 0x000007FE))
#define TFT ((TFT_TypeDef *)TFT_BASE)
/****************************************************************************
 * Function Name  : TFT_WriteCmd
 * Description    : LCD写入命令
 * Input          : cmd：写入的16位命令
 * Output         : None
 * Return         : None
 ****************************************************************************/

static void TFT_WriteCmd(uint16_t cmd) { TFT->TFT_CMD = cmd; }

/****************************************************************************
 * Function Name  : TFT_WriteData
 * Description    : LCD写入数据
 * Input          : dat：写入的16位数据
 * Output         : None
 * Return         : None
 ****************************************************************************/

static void TFT_WriteData(u16 dat) { TFT->TFT_DATA = dat; }

/****************************************************************************
 * Function Name  : TFT_Init
 * Description    : 初始化LCD屏
 * Input          : None
 * Output         : None
 * Return         : None
 ****************************************************************************/
void TFT_Init(void) {
    uint16_t i;

    TFT_WriteCmd(0xE9);
    TFT_WriteData(0x20);

    TFT_WriteCmd(0x11);  // Exit Sleep
    for (i = 500; i > 0; i--);

    TFT_WriteCmd(0x3A);
    TFT_WriteData(0x55);  // 16Bit colors

    TFT_WriteCmd(0xD1);
    TFT_WriteData(0x00);
    TFT_WriteData(0x65);  // 调试此值改善水纹
    TFT_WriteData(0x1F);

    TFT_WriteCmd(0xD0);
    TFT_WriteData(0x07);
    TFT_WriteData(0x07);
    TFT_WriteData(0x80);

    TFT_WriteCmd(0x36);   // Set_address_mode
    TFT_WriteData(0x48);  // 48

    TFT_WriteCmd(0xC1);
    TFT_WriteData(0x10);
    TFT_WriteData(0x10);
    TFT_WriteData(0x02);
    TFT_WriteData(0x02);

    TFT_WriteCmd(0xC0);  // Set Default Gamma
    TFT_WriteData(0x00);
    TFT_WriteData(0x35);
    TFT_WriteData(0x00);
    TFT_WriteData(0x00);
    TFT_WriteData(0x01);
    TFT_WriteData(0x02);

    TFT_WriteCmd(0xC4);
    TFT_WriteData(0x03);

    TFT_WriteCmd(0xC5);  // Set frame rate
    TFT_WriteData(0x01);

    TFT_WriteCmd(0xD2);  // power setting
    TFT_WriteData(0x01);
    TFT_WriteData(0x22);

    TFT_WriteCmd(0xE7);
    TFT_WriteData(0x38);

    TFT_WriteCmd(0xF3);
    TFT_WriteData(0x08);
    TFT_WriteData(0x12);
    TFT_WriteData(0x12);
    TFT_WriteData(0x08);

    TFT_WriteCmd(0xC8);  // Set Gamma
    TFT_WriteData(0x01);
    TFT_WriteData(0x52);
    TFT_WriteData(0x37);
    TFT_WriteData(0x10);
    TFT_WriteData(0x0d);
    TFT_WriteData(0x01);
    TFT_WriteData(0x04);
    TFT_WriteData(0x51);
    TFT_WriteData(0x77);
    TFT_WriteData(0x01);
    TFT_WriteData(0x01);
    TFT_WriteData(0x0d);
    TFT_WriteData(0x08);
    TFT_WriteData(0x80);
    TFT_WriteData(0x00);

    TFT_WriteCmd(0x29);  // display on
}

/****************************************************************************
 * Function Name  : TFT_SetWindow
 * Description    : 设置要操作的窗口范围
 * Input          : xStart：窗口起始X坐标
 *                * yStart：窗口起始Y坐标
 *                * xEnd：窗口结束X坐标
 *                * yEnd：窗口结束Y坐标
 * Output         : None
 * Return         : None
 ****************************************************************************/

static void TFT_SetWindow(uint16_t xStart, uint16_t yStart, uint16_t xEnd, uint16_t yEnd) {
    TFT_WriteCmd(0x2A);

    TFT_WriteData(xStart >> 8);
    TFT_WriteData(xStart & 0XFF);
    TFT_WriteData(xEnd >> 8);
    TFT_WriteData(xEnd & 0XFF);

    TFT_WriteCmd(0x2b);
    TFT_WriteData(yStart >> 8);
    TFT_WriteData(yStart & 0XFF);
    TFT_WriteData(yEnd >> 8);
    TFT_WriteData(yEnd & 0XFF);
    TFT_WriteCmd(0x2c);
}

/****************************************************************************
 * Function Name  : TFT_ClearScreen
 * Description    : 将LCD清屏成相应的颜色
 * Input          : color：清屏颜色
 * Output         : None
 * Return         : None
 ****************************************************************************/

void TFT_ClearScreen(uint16_t color) {
    uint16_t i, j;

    TFT_SetWindow(0, 0, TFT_XMAX, TFT_YMAX);  // 作用区域
    for (i = 0; i < TFT_XMAX + 1; i++) {
        for (j = 0; j < TFT_YMAX + 1; j++) {
            TFT_WriteData(color);
        }
    }
}
